package com.hmdp.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.util.RandomUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hmdp.domain.dto.LoginFormDTO;
import com.hmdp.domain.dto.Result;
import com.hmdp.domain.dto.UserDTO;
import com.hmdp.domain.entity.User;
import com.hmdp.mapper.UserMapper;
import com.hmdp.properties.JwtProperties;
import com.hmdp.service.IUserService;
import com.hmdp.utils.JwtUtil;
import com.hmdp.utils.UserHolder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import static com.hmdp.content.MessageContent.CODE_ERROR;
import static com.hmdp.utils.RedisConstants.*;
import static com.hmdp.utils.SystemConstants.ICON_DEFAULT;
import static com.hmdp.utils.SystemConstants.USER_NICK_NAME_PREFIX;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author 虎哥
 * @since 2021-12-22
 */
@Service
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
    @Autowired
    private StringRedisTemplate redisTemplate;
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private JwtProperties jwtProperties;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    /**
     * 发送验证码
     *
     * @param phone
     * @return
     */
    @Override
    public Result sendcode(String phone) {
        log.info("发送验证码,手机号:{}", phone);
        //生成验证码
        int[] code = RandomUtil.randomInts(6);
        String randomCode = Arrays.toString(code).replaceAll("\\[|\\]|,|\\s", "");
        log.info("验证码:{}", randomCode);
        //这里放入redis中，key 为 login:code:手机号 ,value 为验证码
        //在tomcat集群下，由于负载均衡，很有可能导致验证码的发送和校验不是同一台服务器处理，服务器之间的session不共享导致登录失败
        ValueOperations<String, String> opsForValue = redisTemplate.opsForValue();
        //有效期5min
        opsForValue.set(LOGIN_CODE_KEY + phone, randomCode, LOGIN_CODE_TTL, TimeUnit.MINUTES);
        return Result.ok();
    }

    /**
     * 用户登录
     *
     * @param loginForm
     * @param session
     * @return
     */
    @Override
    public Result login(LoginFormDTO loginForm, HttpSession session) {
        log.info("用户登录:{}", loginForm);
        //从redis获取手机号对应的验证码
        ValueOperations<String, String> stringValueOperations = redisTemplate.opsForValue();
        String code = stringValueOperations.get(LOGIN_CODE_KEY + loginForm.getPhone());
        //校验验证码
        if (code == null || !code.equals(loginForm.getCode())) {
            return Result.fail(CODE_ERROR);
        }
        //校验成功删除验证码
        //防止同一个用户使用同一个验证码多次登录
        if (Boolean.FALSE.equals(redisTemplate.delete(LOGIN_CODE_KEY + loginForm.getPhone()))) {
            return Result.fail("账号已登录");
        }
        //根据手机号获取用户
        QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
        userQueryWrapper.eq("phone", loginForm.getPhone());
        //从数据库中获取到用户信息
        User user = userMapper.selectOne(userQueryWrapper);
        UserDTO userDTO = new UserDTO();
        //TODO 我感觉到这里一个用户一个线程，除非在删除验证码时，又去发送验证码，再去登录，这个时候一个用户两个线程过来注册登录等等，，
        if (user == null) {
            //新用户，进行注册
            user = new User();
            user.setIcon(ICON_DEFAULT);
            user.setPhone(loginForm.getPhone());
            user.setNickName(USER_NICK_NAME_PREFIX + RandomUtil.randomString(16));
            userMapper.insert(user);
        }
        BeanUtil.copyProperties(user, userDTO);
        HashOperations<String, Object, Object> opsForHash = redisTemplate.opsForHash();
        //生成token
        String token = JwtUtil.createJWT(jwtProperties.getUserSecretKey(), jwtProperties.getUserTtl(), new HashMap<>());
        //将用户信息放入redis，key为token，value为userDto
        Map<String, Object> userDtoMap = BeanUtil.beanToMap(userDTO, new HashMap<>(),
                CopyOptions.create().setFieldValueEditor((fieldName, fieldValue) -> fieldValue.toString()));
        opsForHash.putAll(token, userDtoMap);
        redisTemplate.expire(token, jwtProperties.getUserTtl() / 1000, TimeUnit.SECONDS);

        return Result.ok(token);
    }

    @Override
    public Result sign() {
        ValueOperations<String, String> opsForValue = stringRedisTemplate.opsForValue();
        //获取当前用户
        UserDTO user = UserHolder.getUser();
        //获取日期
        LocalDateTime now = LocalDateTime.now();
        String format = now.format(DateTimeFormatter.ofPattern(":yyyyMM"));
        //redis的key为sign:userId:yyyyMM
        String key = USER_SIGN_KEY + user.getId() + format;
        int dayOfMonth = now.getDayOfMonth();

        opsForValue.setBit(key, dayOfMonth - 1, true);
        return Result.ok();
    }

    @Override
    public Result logout(HttpServletRequest request) {
        String token = request.getHeader(jwtProperties.getUserTokenName());
        stringRedisTemplate.delete(token);
        return Result.ok();
    }
}
